package util;

import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.NewImage;
import ij.io.FileInfo;
import ij.process.ImageProcessor;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

import profile.Profile;

/**
 *  This class holds various utility methods for all the plugins
 */
public class QualityControlHelper {
    

    /**
     * Returns the gradient magnitude of a given profile at index i
     * 
     * @param prof
     * @param i
     * @return
     */
    public static double gradientMagnitude(double[] prof, int i) {
        if (i == 0) {
            return sqrt(pow(prof[i + 1] - prof[i], 2)) / sqrt(2);
        } else if (i == (prof.length - 1)) {
            return sqrt(pow(prof[i] - prof[i - 1], 2)) / sqrt(2);
        }
        return sqrt(pow(prof[i + 1] - prof[i], 2) + pow(prof[i] - prof[i - 1], 2)) / sqrt(2);
    }

    /**
     * Returns the laplacian of a given profile at index i
     * 
     * @param prof
     * @param i
     * @return
     */
    public static double laplacian(double[] prof, int i) {
        if (i == (prof.length - 1)) {
            return prof[i] - 2 * prof[i];
        }
        return prof[i + 1] + prof[i] - 2 * prof[i];
    }

    /**
     * 
     */

    public static ImagePlus getMedianImageFromStack(ImageStack stack, String filename) {
        int width = stack.getWidth();
        int height = stack.getHeight();
        int stackSize = stack.getSize();
        ImagePlus resultImg = NewImage.createByteImage(filename + "_Median", width, height, 1, NewImage.FILL_BLACK);
        ArrayList<byte[]> medianLists = new ArrayList<byte[]>(width * height);
        byte[] curSlice = new byte[width * height];

        // TODO: speed improvement here would be to use a min-max-median heap to
        // store
        // the values instead of a 2D array that is sorted later
        int offset, i;
        for (int n = 1; n <= stackSize; n++) {
            curSlice = (byte[]) stack.getPixels(n);
            for (int y = 0; y < height; y++) {
                offset = y * width;
                for (int x = 0; x < width; x++) {
                    i = offset + x;
                    if (medianLists.size() < width * height) {
                        medianLists.add(new byte[stackSize]);
                    }
                    (medianLists.get(i))[n - 1] = curSlice[i];
                }
            }
        }

        ImageProcessor ip = resultImg.getProcessor().createProcessor(width, height);
        byte[] resultArr = (byte[]) ip.getPixels();
        for (int j = 0; j < medianLists.size(); j++) {
            // convert to int array first so that it sorts correctly since all
            // bytes are signed in Java
            int[] arr = toIntArray((byte[]) medianLists.get(j));
            Arrays.sort(arr);
            resultArr[j] = (byte) arr[arr.length / 2];
            
            //set progress bar
            IJ.showProgress(j, medianLists.size());
        }
        
        resultImg.setProcessor(ip);

        return resultImg;
    }

    /**
     * Converts byte array to int array
     * 
     * @param arr
     * @return
     */
    private static int[] toIntArray(byte[] arr) {
        int[] result = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result[i] = arr[i] & 0xFF;
        }
        return result;
    }

    
    public static void exportProfileToText(Profile profile, int currentMad, File file) throws IOException {
        FileWriter fw = new FileWriter(file);
        for (int i = 0; i < profile.getArr().length; i++) {
            fw.write(String.valueOf(profile.getArr()[i]) + "\n");
            fw.flush();
        }
        fw.close();
    }

    /**
     * Gets the filename from the ImagePlus <code>img</code>.  If the file exists on 
     * the hard drive, it will get the directory and filename info.  Otherwise, this
     * will return the image title.
     * 
     * @param img
     * @return
     */
    public static String getFileName(ImagePlus img) {
        FileInfo fileInfo = img.getOriginalFileInfo();
        if(fileInfo == null) {
            return img.getTitle();
        } else {
            return img.getOriginalFileInfo().directory + img.getOriginalFileInfo().fileName;
        }
    }

}
